#include "OgreStableHeaders.h"
#include "OgreAnimation.h"
#include "OgreKeyFrame.h"
#include "OgreException.h"
#include "OgreSkeleton.h"
#include "OgreBone.h"
#include "OgreEntity.h"
#include "OgreSubEntity.h"
#include "OgreMesh.h"
#include "OgreSubMesh.h"
#include "OgreStringConverter.h"

namespace Ogre
{
	Animation::InterpolationMode Animation::msDefaultInterpolationMode = Animation::IM_LINEAR;
	Animation::RotationInterpolationMode 
		Animation::msDefaultRotationInterpolationMode = Animation::RIM_LINEAR;
		
	Animation::Animation(const String& name, Real length)
		: mName(name)
		, mLength(length)
		, mInterpolationMode(msDefaultInterpolationMode)
		, mRotationInterpolationMode(msDefaultRotationInterpolationMode)
		, mKeyFrameTimesDirty(false)
	{
	}
	
	Animation::~Animation()
	{
		destroyAllTracks();
	}
	
	Real Animation::getLength(void) const
	{
		return mLength;
	}
	
	void Animation::setLength(Real len)
	{
		mLength = len;
	}
	
	NodeAnimationTrack* Animation::createNodeTrack(unsigned short handle)
	{
		if (hasNodeTrack(handle))
		{
			OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM,
				"Node track with the specified handle " +
				StringConverter::toString(handle) + " already exists",
				"Animation::createNodeTrack");
		}
		
		NodeAnimationTrack* ret = OGRE_NEW NodeAnimationTrack(this, handle);
		
		mNodeTrackList[handle] = ret;
		return ret;
	}
	
	NodeAnimationTrack* Animation::createNodeTrack(unsigned short handle, Node* node)
	{
		NodeAnimationTrack* ret = createNodeTrack(handle);
		
		ret->setAssociatedNode(node);
		
		return ret;
	}
	
	unsigned short Animation::getNumNodeTracks(void) const
	{
		return (unsigned short)mNodeTrackList.size();
	}
	
	bool Animation::hasNodeTrack(unsigned short handle) const
	{
		return (mNodeTrackList.find(handle) != mNodeTrackList.end());
	}
	
	NodeAnimationTrack* Animation::getNodeTrack(unsigned short handle) const
	{
		NodeTrackList::const_iterator i = mNodeTrackList.find(handle);
		
		if (i == mNodeTrackList.end())
		{
			OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
				"Cannot find node track with the specified handle " +
				StringConverter::toString(handle),
				"Animation::getNodeTrack");
		}
		
		return i->second;
	}
	
	void Animation::destroyNodeTrack(unsigned short handle)
	{
		NodeTrackList::iterator i = mNodeTrackList.find(handle);
		
		if (i != mNodeTrackList.end())
		{
			OGRE_DELETE i->second;
			mNodeTrackList.erase(i);
			_keyFrameListChanged();
		}
	}
	
	void Animation::destroyAllNodeTracks(void)
	{
		NodeTrackList::iterator i;
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			OGRE_DELETE i->second;
		}
		mNodeTrackList.clear();
		_keyFrameListChanged();
	}
	
	NumericAnimationTrack* Animation::createNumericTrack(unsigned short handle)
	{
		if (hasNumericTrack(handle))
		{
			OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM,
				"Numeric track with the specified handle " +
				StringConverter::toString(handle) + " already exists",
				"Animation::createNumericTrack");
		}
		
		NumericAnimationTrack* ret = OGRE_NEW NumericAnimationTrack(this, handle);
		
		mNumericTrackList[handle] = ret;
		return ret;
	}
	
	NumericAnimationTrack* Animation::createNumericTrack(unsigned short handle,
		const AnimableValuePtr& anim)
	{
		NumericAnimationTrack* ret = createNumericTrack(handle);
		
		ret->setAssociatedAnimable(anim);
		
		return ret;
	}
	
	unsigned short Animation::getNumericTracks(void) const
	{
		return (unsigned short)mNumericTrackList.size();
	}
	
	bool Animation::hasNumericTrack(unsigned short handle) const
	{
		return (mNumericTrackList.find(handle) != mNumericTrackList.end());
	}
	
	NumericAnimationTrack* Animation::getNumericTrack(unsigned short handle) const
	{
		NumericTrackList::const_iterator i = mNumericTrackList.find(handle);
		
		if (i == mNumericTrackList.end())
		{
			OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
				"Cannot find numeric track with the specified handle" +
				StringConverter::toString(handle),
				"Animation::getNumericTrack");
		}
		
		return i->second;
	}
	
	void Animation::destroyNumericTrack(unsigned short handle)
	{
		NumericTrackList::iterator i = mNumericTrackList.find(handle);
		
		if (i != mNumericTrackList.end())
		{
			OGRE_DELETE i->second;
			mNumericTrackList.erase(i);
			_keyFrameListChanged();
		}
	}
	
	void Animation::destroyAllNumericTracks(void)
	{
		NumericTrackList::iterator i;
		for (i = mNumericTrackList.begin(); i != mNumericTrackList.end(); ++i)
		{
			OGRE_DELETE i->second;
		}
		
		mNumericTrackList.clear();
		_keyFrameListChanged();
	}
	
	VertexAnimationTrack* Animation::createVertexTrack(unsigned short handle,
		VertexAnimationType animType)
	{
		if (hasVertexTrack(handle))
		{
			OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM,
				"Vertex track with the specified handle " +
				StringConverter::toString(handle) + " already exists",
				"Animation::createVertexTrack");
		}
		
		VertexAnimationTrack* ret = OGRE_NEW VertexAnimationTrack(this, handle, animType);
		
		mVertexTrackList[handle] = ret;
		return ret;
	}
	
	VertexAnimationTrack* Animation::createVertexTrack(unsigned short handle,
		VertexData* data, VertexAnimationType animType)
	{
		VertexAnimationTrack* ret = createVertexTrack(handle, animType);
		
		ret->setAssociatedVertexData(data);
		
		return ret;
	}
	
	unsigned short Animation::getNumVertexTracks(void) const
	{
		return (unsigned short)mVertexTrackList.size();
	}
	
	bool Animation::hasVertexTrack(unsigned short handle) const
	{
		return (mVertexTrackList.find(handle) != mVertexTrackList.end());
	}
	
	VertexAnimationTrack* Animation::getVertexTrack(unsigned short handle) const
	{
		VertexTrackList::const_iterator i = mVertexTrackList.find(handle);
		
		if (i == mVertexTrackList.end())
		{
			OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
				"Cannot find vertex track with the specified handle " +
				StringConverter::toString(handle),
				"Animation::getVertexTrack");
		}
		
		return i->second;
	}
	
	void Animation::destroyVertexTrack(unsigned short handle)
	{
		VertexTrackList::iterator i = mVertexTrackList.find(handle);
		
		if (i != mVertexTrackList.end())
		{
			OGRE_DELETE i->second;
			mVertexTrackList.erase(i);
			_keyFrameListChanged();
		}
	}
	
	void Animation::destroyAllVertexTracks(void)
	{
		VertexTrackList::iterator i;
		for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i)
		{
			OGRE_DELETE i->second;
		}
		mVertexTrackList.clear();
		_keyFrameListChanged();
	}
	
	void Animation::destroyAllTracks(void)
	{
		destroyAllNodeTracks();
		destroyAllNumericTracks();
		destroyAllVertexTracks();
	}
	
	const String& Animation::getName(void) const
	{
		return mName;
	}
	
	void Animation::apply(Real timePos, Real weight, Real scale)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		NodeTrackList::iterator i;
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			i->second->apply(timeIndex, weight, scale);
		}
		NumericTrackList::iterator j;
		for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j)
		{
			j->second->apply(timeIndex, weight, scale);
		}
		VertexTrackList::iterator k;
		for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k)
		{
			k->second->apply(timeindex, weight, scale);
		}
	}
	
	void Animation::applyToNode(Node* node, Real timePos, Real weight, Real scale)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		NodeTrackList::iterator i;
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			i->second->applyToNode(node, timeIndex, weight, scale);
		}
	}
	
	void Animation::apply(Skeleton* skel, Real timePos, Real weight,
		Real scale)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		NodeTrackList::iterator i;
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			Bone* b = skel->getBone(i->first);
			i->second->applyToNode(b, timeIndex, weight, scale);
		}
	}
	
	void Animation::apply(Skeleton* skel, Real timePos, float weight,
		const AnimationState::BoneBlendMask* blendMask, Real scale)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		NodeTrackList::iterator i;
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			Bone* b = skel->getBone(i->first);
			i->second->applyToNode(b, timeIndex, (*blendMask)[b->getHandle()] * weight, scale);
		}
	}
	
	void Animation::apply(Entity* entity, Real timePos, Real weight,
		bool software, bool hardware)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		VertexTrackList::iterator i;
		for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i)
		{
			unsigned short handle = i->first;
			VertexAnimationTrack* track = i->second;
			
			VertexData* swVertexData;
			VertexData* hwVertexData;
			VertexData* origVertexData;
			bool firstAnim = false;
			if (handle == 0)
			{
				firstAnim = !entry->_getBuffersMarkedForAnimation();
				swVertexData = entity->_getSoftwareVertexAnimVertexData();
				hwVertexData = entity->_getHardwareVertexAnimVertexData();
				origVertexData = entity->getMesh()->sharedVertexData;
				entity->_markBuffersUsedForAnimation();
			}
			else
			{
				SubEntity* s = entity->getSubEntity(handle - 1);
				if (!s->isVisible())
					continue;
				firstAnim = !s->_getBuffersMarkedForAnimation();
				swVertexData = s->_getSoftwareVertexAnimVertexData();
				hwVertexData = s->_getHardwareVertexAnimVertexData();
				origVertexData = s->getSubMesh()->vertexData;
				s->_markBuffersUsedForAnimation();
			}
			if (software)
			{
				if (firstAnim && track->getAnimationType() == VAT_POSE)
				{
					const VertexElement* origelem = 
						origVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
					const VertexElement* destelem = 
						swVertexData->vertexDelcaration->findElementBySemantic(VES_POSITION);
					HardwareVertexBufferSharedPtr origBuffer =
						origVertexData->vertexBufferBinding->getBuffer(origelem->getSource());
					HardwareVertexBufferSharedPtr destBuffer = 
						swVertexData->vertexBufferBinding->getBuffer(destelem->getSource());
					destBuffer->copyData(*origBuffer.get(), 0, 0, destBuffer->getSizeInBytes(), true);
				}
				track->setTargetMode(VertexAnimationTrack::TM_SOFTWARE);
				track->applyToVertexData(swVertexData, timeIndex, weight,
					&(entity->getMesh()->getPostList()));
			}
			if (hardware)
			{
				track->setTargetMode(VertexAnimationTrack::TM_HARDWARE);
				track->applyToVertexData(hwVertexData, timeIndex, weight,
					&(entity->getMesh()->getPoseList()));
			}
		}
	}
	
	void Animation::applyToAnimable(const AnimableValuePtr& anim, Real timePos, Real weight, Real scale)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		NumericTrackList::iterator j;
		for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j)
		{
			j->second->applyToAnimable(anim, weight, scale);
		}
	}
	
	void Animation::applyToVertexData(VertexData* data, Real timePos, Real weight)
	{
		TimeIndex timeIndex = _getTimeIndex(timePos);
		
		VertexTrackList::iterator k;
		for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k)
		{
			k->second->applyToVertexData(data, timeIndex, weight);
		}
	}
	
	void Animation::setInterpolationMode(InterpolationMode im)
	{
		mInterpolationMode = im;
	}
	
	Animation::InterpolationMode Animation::getInterpolationMode(void) const
	{
		return mInterpolationMode;
	}
	
	void Animation::setDefaultInterpolationMode(InterpolationMode im)
	{
		msDefaultInterpolationMode = im;
	}
	
	Animation::InterpolationMode Animation::getDefaultInterpolationMode(void)
	{
		return msDefaultInterpolationMode;
	}
	
	const Animation::NodeTrackList& Animation::_getNodeTrackList(void) const
	{
		return mNodeTrackList;
	}
	
	const Animation::NumericTrackList& Animation::_getNumericTrackList(void) const
	{
		return mNumericTrackList;
	}
	
	const Animation::VertexTrackList& Animation::_getVertexTrackList(void) const
	{
		return mVertexTrackList;
	}
	
	void Animation::setRotationInterpolationMode(RotationInterpolationMode im)
	{
		mRotationInterpolationMode = im;
	}
	
	Animation::RotationInterpolationMode Animation::getRotationInterpolationMode(void) const
	{
		return mRotationInterpolationMode;
	}
	
	void Animation::setDefaultRotationInterpolationMode(RotationInterpolationMode im)
	{
		msDefaultRotationInterpolationMode = im;
	}
	
	Animation::RotationInterpolationMode Animation::getDefaultRotationInterpolationMode(void)
	{
		return msDefaultRotationInterpolationMode;
	}
	
	void Animation::optimise(bool discardIdentityNodeTracks)
	{
		optimiseNodeTracks(discardIdentityNodeTracks);
		optimiseVertexTracks();
	}
	
	void Animation::_collectIdentityNodeTracks(TrackHandleList& tracks) const
	{
		NodeTrackList::const_iterator i, iend;
		iend = mNodeTrackList.end();
		for (i = mNodeTrackList.begin(); i != iend; ++i)
		{
			const NodeAnimationTrack* track = i->second;
			if (track->hasNonZeroKeyFrames())
			{
				tracks.erase(i->first);
			}
		}
	}
	
	void Animation::_destroyNodeTracks(const TrackHandleList& tracks)
	{
		TrackHandleList::const_iterator t, tend;
		tend = tracks.end();
		for (t = tracks.begin(); t != tend; ++t)
		{
			destroyNodeTrack(*t);
		}
	}
	
	void Animation::optimiseNodeTracks(bool discardIdentityTracks)
	{
		list<unsigned short>::type tracksToDestroy;
		NodeTrackList::iterator i;
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			NodeAnimationTrack* track = i->second;
			if (discardIdentityTracks && !track->hasNonZeroKeyFrames())
			{
				tracksToDestroy.push_back(i->first);
			}
			else
			{
				track->optimise();
			}
		}
		
		for(list<unsigned short>::type::iterator h = tracksToDestroy.begin();
			h != tracksToDestroy.end(); ++h)
		{
			destroyNodeTrack(*h);
		}
	}
	
	void Animation::optimiseVertexTracks(void)
	{
		list<unsigned short>::type tracksToDestroy;
		VertexTrackList::iterator i;
		for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i)
		{
			VertexAnimationTrack* track = i->second;
			if (!track->hasNonZeroKeyFrames())
			{
				tracksToDestroy.push_back(i->first);
			}
			else
			{
				track->optimise();
			}
		}
		
		for (list<unsigned short>::type::iterator h = tracksToDestroy.begin();
			h != tracksToDestroy.end(); ++h)
		{
			destroyVertexTrack(*h);
		}
	}
	
	Animation* Animation::clone(const String& newName) const
	{
		Animation* newAnim = OGRE_NEW Animation(newName, mLength);
		newAnim->mInterpolationMode = mInterpolationMode;
		newAnim->mRotationInterpolationMode = mRotationInterpolationMode;
		
		for (NodeTrackList::const_iterator i = mNodeTrackList.begin();
			i != mNodeTrackList.end(); ++i)
		{
			i->second->_clone(newAnim);
		}
		for (NumericTrackList::const_iterator i = mNumericTrackList.begin();
			i != mNumericTrackList.end(); ++i)
		{
			i->second->_clone(newAnim);
		}
		for (VertexTrackList::const_iterator i = mVertexTrackList.begin();
			i != mVertexTrackList.end(); ++i)
		{
			i->second->_clone(newAnim);
		}
		
		newAnim->_keyFrameListChanged();
		return newAnim;
	}
	
	TimeIndex Animation::_getTimeIndex(Real timePos) const
	{
		if (mKeyFrameTimesDirty)
		{
			buildKeyFrameTimeList();
		}
		
		Real totalAnimationLength = mLength;
		
		while (timePos > totalAnimationLength && totalAnimationLength > 0.0f)
		{
			timePos -= totalAnimationLength;
		}
		
		KeyFrameTimeList::iterator it = 
			std::lower_bound(mKeyFrameTimes.begin(), mkeyFrameTimes.end(), timePos);
		
		return TimeIndex(timePos, std::distance(mKeyFrameTimes.begin(), it));
	}
	
	void Animation::buildKeyFrameTimeList(void) const
	{
		NodeTrackList::const_itarator i;
		NumericTrackList::const_iterator j;
		VertexTrackList::const_iterator k;
		
		mKeyFrameTimes.clear();
		
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			i->second->_collectKeyFrameTimes(mKeyFrameTimes);
		}
		for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j)
		{
			j->second->_collectKeyFrameTimes(mKeyFrameTimes);
		}
		for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k)
		{
			k->second->_collectKeyFrameTimes(mKeyFrameTimes);
		}
		
		for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
		{
			i->second->_buildKeyFrameIndexMap(mKeyFrameTimes);
		}
		for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j)
		{
			i->second->_buildKeyFrameIndexMap(mKeyFrameTimes);
		}
		for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k)
		{
			k->second->_buildKeyFrameIndexMap(mKeyFrameTimes);
		}
		
		mKeyFrameTimesDirty = false;
	}
}
